package com.anlogic.sdk.bootimage.internal.ui.composite;

import com.anlogic.sdk.bootimage.internal.ui.common.BootgenComboViewer;
import com.anlogic.sdk.bootimage.internal.ui.common.BootgenTextField;
import com.anlogic.sdk.bootimage.internal.ui.common.IModifyListener;
import com.anlogic.sdk.bootimage.model.BootGenModel;
import com.anlogic.sdk.bootimage.model.IPartitionProperties;
import com.anlogic.sdk.bootimage.model.Partition;
import com.anlogic.sdk.bootimage.model.options.AuthenticationType;
import com.anlogic.sdk.bootimage.model.options.CheckSumType;
import com.anlogic.sdk.bootimage.model.options.DestDeviceType;
import com.anlogic.sdk.bootimage.model.options.EncryptionType;
import com.anlogic.sdk.bootimage.model.options.IBootgenOption;
import com.anlogic.sdk.bootimage.model.options.PartionType;
import com.anlogic.sdk.bootimage.model.options.PartitionOwner;
import com.anlogic.sdk.utils.FileUtils;
import com.anlogic.sdk.utils.UIUtils;
import com.anlogic.sdk.utils.options.ChipType;
import com.anlogic.sdk.utils.options.CpuType;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;

public class PartitionConfigComposite extends AbsBootgenComposite implements IPartitionProperties {
	protected Partition partition;

	private BootgenTextField fileTextField;

	private BootgenComboViewer<PartionType> partitionComboField;

	private BootgenComboViewer<DestDeviceType> DestDeviceComboField;

	private BootgenComboViewer<CpuType> DestCpuComboField;

	private BootgenComboViewer<AuthenticationType> authComboField;

	private BootgenComboViewer<EncryptionType> encryptComboField;

	private BootgenComboViewer<CheckSumType> checkSumComboField;
	
	private BootgenComboViewer<PartitionOwner> partitionOwnerComboField;
	
	private BootgenTextField alignmentField;

	private BootgenTextField offsetField;

	private BootgenTextField loadField;
	
	private BootgenTextField execField;
	
	private Composite generalComboComposite;
	
	private static final String[] PARTITION_EXT = new String[] { "*bit;*.elf;*.bin;*.txt;*.pem;*.pub;*.sig;*.nky", "*.*" };

	public PartitionConfigComposite(Composite parent, BootGenModel model, Partition partition) {
		super(parent, 0, model);
		this.partition = partition;
		createUI(this);
	}

	protected void createUI(Composite container) {
		GridLayoutFactory.fillDefaults().margins(5, 5).numColumns(4).applyTo(container);
		createGeneralSection(container);
		createOtherGroup(container);
	}

	private void createGeneralSection(Composite container) {
		this.fileTextField = new BootgenTextField(this);
		this.fileTextField.createOpenBrowseField(container, "File path", "bootgen_files_selection", PARTITION_EXT);
		addAutoCompletionCapability(this.fileTextField.getControl());
		this.fileTextField.setDefaultGridData(2);
		this.generalComboComposite = new Composite(container, 0);
		GridDataFactory.fillDefaults().grab(true, false).span(4, 1).applyTo((Control) this.generalComboComposite);
		GridLayoutFactory.fillDefaults().numColumns(4).applyTo(this.generalComboComposite);
		this.partitionComboField = new BootgenComboViewer<PartionType>(this);
		this.partitionComboField.createField(this.generalComboComposite, "Partition type: ", (Object[]) PartionType.values());
		setComboWidth((BootgenComboViewer<PartionType>) this.partitionComboField, 3);
		createGeneralFields(this.generalComboComposite);
		this.authComboField = new BootgenComboViewer<AuthenticationType>(this);
		this.authComboField.createField(this.generalComboComposite, "Authentication: ",(Object[]) AuthenticationType.values());
		setComboWidth((BootgenComboViewer<AuthenticationType>) this.authComboField, 1);
		this.encryptComboField = new BootgenComboViewer<EncryptionType>(this);
		this.encryptComboField.createField(this.generalComboComposite, "Encryption: ", (Object[]) EncryptionType.values());
		setComboWidth((BootgenComboViewer<EncryptionType>) this.encryptComboField, 1);
		this.checkSumComboField = new BootgenComboViewer<CheckSumType>(this);
		this.checkSumComboField.createField(this.generalComboComposite, "Checksum: ",(Object[]) CheckSumType.values());
		setComboWidth((BootgenComboViewer<CheckSumType>) this.checkSumComboField, 1);
		this.partitionOwnerComboField = new BootgenComboViewer<PartitionOwner>(this);
		this.partitionOwnerComboField.createField(this.generalComboComposite, "Partition Owner: ",(Object[]) PartitionOwner.values());
		setComboWidth((BootgenComboViewer<PartitionOwner>) this.partitionOwnerComboField, 1);
	}

	private void setComboWidth(BootgenComboViewer<?> comboField, int hSpan) {
		int hHint = UIUtils.convertWidthInCharsToPixels((Control) this, 20);
		GridDataFactory.swtDefaults().span(hSpan, 1).hint(hHint, -1).applyTo((Control) comboField.getControl());
	}

	private void createGeneralFields(Composite container) {
		this.DestDeviceComboField = new BootgenComboViewer<DestDeviceType>(this);
		this.DestDeviceComboField.createField(container, "Destination Device: ", (Object[]) DestDeviceType.values());
		setComboWidth((BootgenComboViewer<DestDeviceType>) this.DestDeviceComboField, 1);
		this.DestCpuComboField = new BootgenComboViewer<CpuType>(this);
		List<CpuType> cpuTypes = ChipType.getCpuList(model.getChipType().getName());
		this.DestCpuComboField.createField(container, "Destination CPU: ", cpuTypes.toArray(new Object[0]));
		setComboWidth((BootgenComboViewer<CpuType>) this.DestCpuComboField, 1);
	}

	private void createOtherGroup(Composite container) {
		Group otherGroup = new Group(container, 0);
		GridDataFactory.fillDefaults().grab(true, false).span(4, 1).applyTo((Control) otherGroup);
		GridLayoutFactory.fillDefaults().margins(5, 5).numColumns(4).applyTo((Composite) otherGroup);
		otherGroup.setText("Other");
		this.alignmentField = new BootgenTextField(this);
		this.alignmentField.createField((Composite) otherGroup, "Alignment: ");
		this.alignmentField.setDefaultGridData(1);
		this.offsetField = new BootgenTextField(this);
		this.offsetField.createField((Composite) otherGroup, "Offset: ");
		this.offsetField.setDefaultGridData(1);
		this.loadField = new BootgenTextField(this);
		this.loadField.createField((Composite) otherGroup, "Load: ");
		this.loadField.setDefaultGridData(1);
		this.execField = new BootgenTextField(this);
		this.execField.createField((Composite) otherGroup, "Execution: ");
		this.execField.setDefaultGridData(1);
	}

	public void addListeners() {
		addGeneralSectionListeners();
		addOtherGroupListeners();
	}

	private void addGeneralSectionListeners() {
		this.fileTextField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				PartitionConfigComposite.this.partition.setFilePath(newValue);
				PartitionConfigComposite.this.autoSelectPartitionAttributes();
				PartitionConfigComposite.this.validateUI();
				
			}
		});
		this.partitionComboField.setModifyListener(new IModifyListener<PartionType>() {
			public void valueChanged(PartionType newValue) {
				PartitionConfigComposite.this.partition.setPartitionType(newValue);
				PartitionConfigComposite.this.refreshDestDeviceField(newValue);
				PartitionConfigComposite.this.refreshDestCpuField(PartitionConfigComposite.this.partition.getDestDeviceType());
				if (newValue.equals(PartionType.BOOTLOADER)) {
					PartitionConfigComposite.this.partitionOwnerComboField.setSelection(PartitionOwner.NONE);
				} else {
					PartitionConfigComposite.this.partitionOwnerComboField.setSelection(PartitionOwner.FSBL);
				}
			}
		});
		this.authComboField.setModifyListener(new IModifyListener<AuthenticationType>() {
			public void valueChanged(AuthenticationType newValue) {
				PartitionConfigComposite.this.partition.setAuthenticationType(newValue);
			}
		});
		this.DestDeviceComboField.setModifyListener(new IModifyListener<DestDeviceType>() {
			public void valueChanged(DestDeviceType newValue) {
				PartitionConfigComposite.this.partition.setDestDeviceType(newValue);
				PartitionConfigComposite.this.refreshDestCpuField(PartitionConfigComposite.this.DestDeviceComboField.getSelection());
			}
		});
		addCpuComboPropertyListener(this.DestCpuComboField, "destination_cpu");
		addComboPropertyListener(this.encryptComboField, "encryption");
		addComboPropertyListener(this.checkSumComboField, "checksum");
		addComboPropertyListener(this.partitionOwnerComboField, "partition_owner");
	}

	private void addOtherGroupListeners() {
		addTextPropertyListener(this.alignmentField, "alignment");
		addTextPropertyListener(this.offsetField, "offset");
		addTextPropertyListener(this.loadField, "load");
		addTextPropertyListener(this.execField, "exec");
	}

	private void addTextPropertyListener(BootgenTextField textField, final String property) {
		textField.setModifyListener(new IModifyListener<String>() {
			public void valueChanged(String newValue) {
				PartitionConfigComposite.this.partition.setPropertyValue(property, newValue);
				PartitionConfigComposite.this.validateUI();
			}
		});
	}

	private <T extends IBootgenOption> void addComboPropertyListener(BootgenComboViewer<T> comboViewer,
			final String property) {
		comboViewer.setModifyListener(new IModifyListener<T>() {
			public void valueChanged(T newValue) {
				PartitionConfigComposite.this.partition.setPropertyValue(property, newValue.getId());
			}
		});
	}

	private void addCpuComboPropertyListener(BootgenComboViewer<CpuType> comboViewer,
			final String property) {
		comboViewer.setModifyListener(new IModifyListener<CpuType>() {
			public void valueChanged(CpuType newValue) {
				PartitionConfigComposite.this.partition.setPropertyValue(property, newValue.getName());
			}
		});
	}
	
	public void loadUI() {
		loadGeneralSection();
		loadOtherGroup();
	}

	private void loadGeneralSection() {
		this.fileTextField.setSelection(this.partition.getFilePath());
		this.partitionComboField.setSelection(this.partition.getPartitionType());
		refreshAuthComboField(this.model.getAuthType());
		refreshEncryptComboField(this.model.getEncryptionType());
		this.checkSumComboField.setSelection(this.partition.getCheckSumType());
		this.partitionOwnerComboField.setSelection(this.partition.getPartitionOwner());
		refreshDestDeviceField(this.partition.getPartitionType());
		this.DestDeviceComboField.setSelection(this.partition.getDestDeviceType());
		refreshDestCpuField(this.partition.getDestDeviceType());
		this.DestCpuComboField.setSelection(this.partition.getDestCpuType());
		this.authComboField.setEnabled(this.model.isUseAuthentication());
		this.encryptComboField.setEnabled(this.model.isUseEncryption());
	}

	private void refreshDestDeviceField(PartionType paritionType) {
		boolean isFsbl = (paritionType == PartionType.BOOTLOADER);
		if (isFsbl) {
			this.DestDeviceComboField.setInput((Object[]) new DestDeviceType[] { DestDeviceType.PS });
		} else {
			this.DestDeviceComboField.setInput((Object[]) DestDeviceType.values());
		}
		boolean isEnabled = paritionType != PartionType.INIT;
		this.DestDeviceComboField.setEnabled(isEnabled);
	}
	
	private void refreshDestCpuField(DestDeviceType deviceType) {
		boolean isEnabled = (deviceType == DestDeviceType.PS);
		this.DestCpuComboField.setEnabled(isEnabled);
	}
	
	private void refreshAuthComboField(AuthenticationType type) {
		List<Object> options = new ArrayList<Object>();
		options.add(AuthenticationType.NONE);
		if (type != AuthenticationType.NONE) {
			options.add(type);
		}
		this.authComboField.setInput(options.toArray(new Object[0]));
		this.authComboField.setSelection(this.partition.getAuthenticationType());
	}
	
	private void refreshEncryptComboField(EncryptionType type) {
		List<Object> options = new ArrayList<Object>();
		if (partition.isFsbl()) {
			options.add(type);
		} else {
			options.add(EncryptionType.NONE);
			if (type != EncryptionType.NONE) {
				options.add(type);
			}
		}	
		this.encryptComboField.setInput(options.toArray(new Object[0]));
		this.encryptComboField.setSelection(this.partition.getEncryptionType());
	}

	private void loadOtherGroup() {
		this.alignmentField.setSelection(this.partition.getAlignment());
		this.offsetField.setSelection(this.partition.getOffSet());
		this.loadField.setSelection(this.partition.getLoad());
		this.execField.setSelection(this.partition.getExec());
	}

	public void okPressed() {
		super.okPressed();
	}

	private void autoSelectPartitionAttributes() {
		File file = new File(this.fileTextField.getSelection());
		String fileExtension = FileUtils.getFileExtension(file.getName());
		String iName = file.getName().toLowerCase();
		if ("elf".equalsIgnoreCase(fileExtension) && iName.contains("fsbl")) {
			this.partitionComboField.setSelection(PartionType.BOOTLOADER);
			this.partitionOwnerComboField.setSelection(PartitionOwner.NONE);
		} else if ("init".equalsIgnoreCase(fileExtension)) {
			this.partitionComboField.setSelection(PartionType.INIT);
			this.partitionOwnerComboField.setSelection(PartitionOwner.FSBL);
		} else {
			this.partitionComboField.setSelection(PartionType.DATAFILE);
			this.partitionOwnerComboField.setSelection(PartitionOwner.FSBL);
		}
		if ("bit".equalsIgnoreCase(fileExtension)) {
			this.DestDeviceComboField.setSelection(DestDeviceType.PL);
		}
	}
}
